/*
 * Copyright (c) 2018-2019 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#if defined(CONFIG_BT_CTLR_DF_SUBSYSTEM)

#include <zephyr/types.h>
#include <stddef.h>
#include <errno.h>
#include <stdbool.h>
#include <soc.h>

#include <bluetooth/dfe_data.h>
#include <bluetooth/dfe_config.h>

#include "hal/cpu.h"
#include "hal/ccm.h"
#include "hal/radio.h"

#include "util/memq.h"
#include "lll.h"

/*****************************************************************************
 * MACROS
 ****************************************************************************/
/* provided to set 2M PHY in advertising-scanning mode */
#define BT_DFE_2M_PHY (BIT(1))

#define DFE_BUFFER_SIZE_WORDS( array ) (sizeof(array)/(WORD_SIZE))
#define DFE_GPIO_MAX 8 //!< Number of supported GPIOS that may be used to drive antenna switches
#define DFE_ANT_PATTERN_MAX 40 //!< Max number of patterns that may be use to switch antennas
#define DFE_DURATION_MIN_UNIT_US 8 //!< smallest duration of DFE unit (number of 8us) in [us]

/*****************************************************************************
 * DATA STRUCTURES
 ****************************************************************************/
/** @brief DFE sampling configuration data structure
 */
struct dfe_sampling_config {
	bool en_sampling_on_crc_error; //!< This is used even if CTEINLINE is turned off
	/** @brief Limits start DFE to be triggered by TASKS_DFESTART only
	 */
	bool trig_task_only;
	/** @brief Interval between samples in REFERENCE period
	 */
	u8_t sample_spacing_ref;
	/** @brief Mode: I/Q or magnitude/phase
	 */
	u8_t sampling_type;
	/** @brief Interval between samples in SWITCH period
	 */
	u8_t sample_spacing;
	/** @brief backoff gain at the beginning of DFE by N steps
	 */
	u8_t backoff_gain;
	/** @brief Time delay relative to beginning of reference period before starting
	 * sampling, number of 16M cycles ?
	 */
	s16_t sample_offset;
};

/** @brief DFE antennae configuration data structure
 */
struct dfe_antenae {
	/** @brief Time between consecutive antenna switches in SWITCH period of CTE
	 */
	u16_t switch_spacing;
	/** @brief Time delay after end of CRC before starting switching, number of 16M cycles
	 */
	s16_t switch_offset;
	/** @brief Array stores indices of antennas to be enabled in particular switch state.
	 * The array does not include reference period antenna and idle antenna.
	 */
	u8_t gpio_patterns[DFE_ANT_PATTERN_MAX];
	u8_t gpio_patterns_len;
	/** @brief GPIOS used to switch antennae.
	 */
	struct dfe_gpio gpio[DFE_GPIO_MAX];
	u8_t gpio_len;
	/** @brief repeat antenna pattern N times
	 */
	u8_t repeat_pattern;
};

/** @brief DFE receive mode configuration structure
 */
struct dfe_conf_rx {
	/** @brief start point of DFE, after CRC or packet payload
	 */
	u8_t start_of_dfe;
	/** @brief duration of whole DFE in number of 8[us]
	 */
	u8_t dfe_duration;
	/** @brief operation mode of DFE: disable, AOD, AOA
	 */
	u8_t dfe_op_mode;
	struct dfe_sampling_config sampl_conf;
	struct dfe_antenae ant_conf;
};

/** @brief DFE broadcast mode configuration structure
 */
struct dfe_conf_tx {
	/** @brief start point of DFE, after CRC or packet payload
	 */
	u8_t start_of_dfe;
	/** @brief duration of whole DFE in number of 8[us]
	 */
	u8_t dfe_duration;
	/** @brief operation mode of DFE: disable, AOD, AOA
	 */
	u8_t dfe_op_mode;
};

/*****************************************************************************
 *GLOBALS
 ****************************************************************************/

#if defined(CONFIG_BT_CTLR_DFE_RX)
static struct dfe_conf_rx dfe_rx;
#endif /* CONFIG_BT_CTLR_DFE_RX */

#if defined(CONFIG_BT_CTLR_DFE_TX)
static struct dfe_conf_tx dfe_tx;
#endif /* CONFIG_BT_CTLR_DFE_TX */
/*****************************************************************************
 *DFE
 ****************************************************************************/
int dfe_set_mode(u8_t mode)
{
	if (mode != RADIO_DFEMODE_DFEOPMODE_Disabled &&
	    mode != RADIO_DFEMODE_DFEOPMODE_AoD &&
	    mode != RADIO_DFEMODE_DFEOPMODE_AoA) {
		return -EINVAL;
	}

#if defined(CONFIG_BT_CTLR_DFE_TX)
	dfe_tx.dfe_op_mode = mode;
#endif /* CONFIG_BT_CTLR_DFE_TX */
#if defined(CONFIG_BT_CTLR_DFE_RX)
	dfe_rx.dfe_op_mode = mode;
#endif /* CONFIG_BT_CTLR_DFE_RX */
	return 0;
	return 0;
}

int dfe_set_duration(u8_t numb_of_8us_periods)
{
	if (numb_of_8us_periods > DFE_MAX_8US_PERIODS)
		return -EINVAL;
	else if(numb_of_8us_periods < DFE_MIN_8US_PERIODS)
		return -EINVAL;

#if defined(CONFIG_BT_CTLR_DFE_TX)
	dfe_tx.dfe_duration = numb_of_8us_periods;
#endif /* CONFIG_BT_CTLR_DFE_TX */
#if defined(CONFIG_BT_CTLR_DFE_RX)
	dfe_rx.dfe_duration = numb_of_8us_periods;
#endif /* CONFIG_BT_CTLR_DFE_RX */
	return 0;
}

u16_t dfe_get_duration_us()
{
	u16_t duration;

#if IS_ENABLED(CONFIG_BT_CTLR_DFE_TX)
	duration = dfe_tx.dfe_duration;
#endif /* CONFIG_BT_CTLR_DFE_TX */
#if defined(CONFIG_BT_CTLR_DFE_RX)
	duration = dfe_rx.dfe_duration;
#endif /* CONFIG_BT_CTLR_DFE_RX */
	return duration * DFE_DURATION_MIN_UNIT_US;
}

int dfe_set_start_point(u8_t start_point)
{
	if (start_point != RADIO_DFECTRL1_DFEINEXTENSION_Payload &&
	    start_point != RADIO_DFECTRL1_DFEINEXTENSION_CRC) {
		return -EINVAL;
	}
#if defined(CONFIG_BT_CTLR_DFE_TX)
	dfe_tx.start_of_dfe = start_point;
#endif /* CONFIG_BT_CTLR_DFE_TX */
#if defined(CONFIG_BT_CTLR_DFE_RX)
	dfe_rx.start_of_dfe = start_point;
#endif /* CONFIG_BT_CTLR_DFE_RX */
	return 0;
}

#if defined(CONFIG_BT_CTLR_DFE_RX)
void dfe_set_sample_on_crc_error(bool value)
{
	dfe_rx.sampl_conf.en_sampling_on_crc_error = value;
}

void dfe_set_trig_dfe_start_task_only(bool value)
{
	dfe_rx.sampl_conf.trig_task_only = value;
}

int dfe_set_sampling_spacing_ref(u8_t spacing)
{
	if((spacing < RADIO_DFECTRL1_TSAMPLESPACINGREF_4us) ||
	  (spacing > RADIO_DFECTRL1_TSAMPLESPACINGREF_250ns)) {
		return -EINVAL;
	}
	dfe_rx.sampl_conf.sample_spacing_ref = spacing;
	return 0;
}

int dfe_set_sampling_type(u8_t type)
{
	if((type != RADIO_DFECTRL1_SAMPLETYPE_IQ) &&
	  (type != RADIO_DFECTRL1_SAMPLETYPE_MagPhase)) {
		return -EINVAL;
	}
	dfe_rx.sampl_conf.sampling_type = type;
	return 0;
}

int dfe_set_sample_spacing(u8_t spacing)
{
	if((spacing < RADIO_DFECTRL1_TSAMPLESPACING_4us) ||
	  (spacing > RADIO_DFECTRL1_TSAMPLESPACING_250ns)) {
		return -EINVAL;
	}
	dfe_rx.sampl_conf.sample_spacing = spacing;
	return 0;
}

int dfe_set_backoff_gain(u8_t value)
{
	if(value > DFE_MAX_BACKOFF_STEPS) {
		return -EINVAL;
	}
	dfe_rx.sampl_conf.backoff_gain = value;
	return 0;
}

int dfe_set_ant_switch_spacing(u8_t spacing)
{
	if((spacing < RADIO_DFECTRL1_TSWITCHSPACING_4us) ||
	  (spacing > RADIO_DFECTRL1_TSWITCHSPACING_1us)) {
		return -EINVAL;
	}
	dfe_rx.ant_conf.switch_spacing = spacing;
	return 0;
}

int dfe_set_switch_offset(u8_t offset)
{
	if((offset < DFE_SWITCH_OFFSET_MIN) ||
	  (offset > DFE_SWITCH_OFFSET_MAX)) {
		return -EINVAL;
	}
	dfe_rx.ant_conf.switch_offset = offset;
	return 0;
}

int dfe_set_sample_offset(u8_t offset)
{
	if((offset < DFE_SAMPLE_OFFSET_MIN) &&
	  (offset > DFE_SAMPLE_OFFSET_MIN)) {
		return -EINVAL;
	}
	dfe_rx.sampl_conf.sample_offset = offset;
	return 0;
}

int dfe_set_ant_gpios(const struct dfe_ant_gpio *gpio, u8_t len)
{
	if (len >= DFE_ANT_GPIO_MAX) {
		return -EINVAL;
	}

	for(u8_t idx=0; idx < len; ++idx) {
		dfe_rx.ant_conf.gpio[idx].reg_idx = gpio[idx].idx;
		dfe_rx.ant_conf.gpio[idx].gpio_num = gpio[idx].gpio_num;
	}
	dfe_rx.ant_conf.gpio_len = len;

	return 0;
}

#define DFE_IDLE_ANT_PATTERN_IDX 0
#define DFE_REF_ANT_PATTERN_IDX 1
#define DFE_SWITCH_ANT_PATTERN_OFFSET 2
int dfe_set_ant_gpio_patterns(u8_t idle, u8_t ref, u8_t *switching, u8_t switching_len)
{
	if (switching == NULL || (switching_len + 2) >= DFE_ANT_PATTERN_MAX) {
		return -EINVAL;
	}

	dfe_rx.ant_conf.gpio_patterns[DFE_IDLE_ANT_PATTERN_IDX] = idle;
	dfe_rx.ant_conf.gpio_patterns[DFE_REF_ANT_PATTERN_IDX] = ref;
	for(u8_t idx=0; idx < switching_len; ++idx) {
		dfe_rx.ant_conf.gpio_patterns[idx + DFE_SWITCH_ANT_PATTERN_OFFSET] = switching[idx];
	}
	dfe_rx.ant_conf.gpio_patterns_len = switching_len + DFE_SWITCH_ANT_PATTERN_OFFSET;
	return 0;
}
#endif /* CONFIG_BT_CTLR_DFE_RX */

void dfe_common_init(void)
{
#if defined(CONFIG_BT_CTLR_DFE_TX)
	dfe_tx.start_of_dfe = RADIO_DFECTRL1_DFEINEXTENSION_CRC;
#endif /* CONFIG_BT_CTLR_DFE_TX */
}

#if defined(CONFIG_BT_CTLR_DFE_TX)
void dfe_init_adv_tx(void)
{
	if (dfe_tx.dfe_op_mode == RADIO_DFEMODE_DFEOPMODE_AoA &&
	    dfe_tx.dfe_duration >= DFE_MIN_8US_PERIODS) {
		radio_dfe_mode_set(dfe_tx.dfe_op_mode);
		radio_cte_inline_ctrl_set(RADIO_CTEINLINECONF_CTEINLINECTRLEN_Disabled);
		radio_dfe_numberof8us_set(dfe_tx.dfe_duration);
		radio_dfe_inextension_set(dfe_tx.start_of_dfe);
	} else {
		radio_dfe_mode_set(RADIO_DFEMODE_DFEOPMODE_Disabled);
	}
}
#endif /* CONFIG_BT_CTLR_DFE_TX */

#if defined(CONFIG_BT_CTLR_DFE_RX)
void dfe_init_scan_rx(void)
{
	if (dfe_rx.dfe_op_mode == RADIO_DFEMODE_DFEOPMODE_AoA &&
	    dfe_rx.dfe_duration >= DFE_MIN_8US_PERIODS) {
		radio_dfe_mode_set(RADIO_DFEMODE_DFEOPMODE_AoA);
		radio_cte_inline_ctrl_set(RADIO_CTEINLINECONF_CTEINLINECTRLEN_Disabled);
		radio_cte_infos1_set(RADIO_CTEINLINECONF_CTEINFOINS1_NotInS1);
		radio_dfe_numberof8us_set(dfe_rx.dfe_duration);
		radio_dfe_inextension_set(dfe_rx.start_of_dfe);

		//set sampling
		radio_dfe_backoff_gain_set(dfe_rx.sampl_conf.backoff_gain);
		radio_cte_error_handling_set(dfe_rx.sampl_conf.en_sampling_on_crc_error);
		radio_dfe_tsampleoffset_set(dfe_rx.sampl_conf.sample_offset);
		radio_dfe_sampletype_set(dfe_rx.sampl_conf.sampling_type);
		radio_dfe_tsamplespacingref_set(dfe_rx.sampl_conf.sample_spacing_ref);
		radio_dfe_tsamplespacing_set(dfe_rx.sampl_conf.sample_spacing);

		//ant switching
		radio_dfe_tswitchspacing_set(dfe_rx.ant_conf.switch_spacing);
		radio_dfe_gpio_set(dfe_rx.ant_conf.gpio,
				     dfe_rx.ant_conf.gpio_len);
		radio_dfe_switch_patterns_set(dfe_rx.ant_conf.gpio_patterns,
					      dfe_rx.ant_conf.gpio_patterns_len);
	} else {
		radio_dfe_mode_set(RADIO_DFEMODE_DFEOPMODE_Disabled);
	}
}

void dfe_packet_buffer_set(struct node_rx_pdu *node_rx)
{
	radio_dfe_samples_buffer_set(node_rx->df_data_packet.data,
			DFE_BUFFER_SIZE_WORDS(node_rx->df_data_packet.data));
	/* reset length because it is only one source of information
	 * how many samples are in buffer.
	 */
	node_rx->df_data_packet.hdr.length = 0;
}

void dfe_setup_package_header(struct node_rx_pdu *node_rx)
{
	NRF_RADIO->EVENTS_CTEPRESENT = 0;
	node_rx->df_data_packet.hdr.length = radio_dfe_get_recv_sampl_num();
	node_rx->df_data_packet.hdr.frequency = radio_dfe_frequency();
}
#endif /* CONFIG_BT_CTLR_DFE_RX */
#endif /* CONFIG_BT_CTLR_DF_SUBSYSTEM */




